package main.db;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;

import com.eroi.migrate.Configure;

public class BeanBuilder {
	
	/**
	 * 生成文件路径 ,如:D:/workspace/16ds
	 */
	public static final String PROPERTY_BUILDER_SRC_PATH = "bean.builde.src.path";
	/**
	 * 实体包路径，如：com.fangdo.entity
	 */
	public static final String PROPERTY_BUILDER_ENTITY_PACKAGE = "bean.builde.entity.package";
	/**
	 * form包路径com.fangdo.action.form
	 */
	public static final String PROPERTY_BUILDER_FORM_PACKAGE = "bean.builde.form.package";
	/**
	 * 是否生成form,取值true|false
	 */
	public static final String PROPERTY_BUILDER_FORM = "bean.builde.form";
	/**
	 * 是否生成实体,取值true|false
	 */
	public static final String PROPERTY_BUILDER_ENTITY = "bean.builde.entity";
	/**
	 * 生成form过程中，如果同名文件已经存在，是否删除重新生成，取值：true|false
	 */
	public static final String PROPERTY_BUILDER_FORM_EXIST = "bean.builde.form.rebuild";
	public static final String PROPERTY_BUILDER_ENTITY_EXIST = "bean.builde.entity.rebuild";
	public static final String PROPERTY_BUILDER_FILTER = "bean.builde.filter";
	
	
	private static Connection conn;
	private static String driver = null;
	private static String url = null;
	private static String username = null;
	private static String password = null;
	private static String entityPackage = null;
	private static String formPackage = null;
	private static String srcPath = null;
	private static boolean isBuildForm = false;
	private static boolean isBuildEntity = true;	
	private static boolean isRebuildForm = false;
	private static boolean isRebuildEntity = true;
	private static ArrayList<String> tableFilter = new ArrayList<String>();
	
	private static final String line = System.getProperty("line.separator");
	private static final String STR_ENTITY_IMPORT = "str_import";
	private static final String STR_ENTITY_FIELD = "str_field";
	private static final String STR_ENTITY_FUN = "str_fun";	
	private static final String STR_ENTITY_TOSTRING = "str_toString";	
	private static int toStringFieldLen = 2;

	static{
		loadPropery();
	}
	
	public static void main(String[] args) throws SQLException {
		Connection connection = null;		
		try{
			connection= BeanBuilder.getConnection();
			List<String> tables = BeanBuilder.tables(connection);
			File formPath = new File(srcPath+"\\"+formPackage.replaceAll("\\.", "\\\\"));
			File entityPath = new File(srcPath+"\\"+entityPackage.replaceAll("\\.", "\\\\"));
			if(!formPath.exists())
				formPath.mkdirs();
			if( !entityPath.exists() )
				entityPath.mkdirs();
			
			for(String table:tables){
				System.out.println("==============="+table+"===============");
				if(isBuildForm){		
					String from = buildBean(connection,table,true);
					saveToFile(from,formPath,table,true);
				}				
				if(isBuildEntity){
					String entity = buildBean(connection,table,false);
					saveToFile(entity,entityPath,table,false);
				}
			}
			
		}catch (Exception e) {
			e.printStackTrace();
		}finally{
			connection.close();
		}
		
//		System.out.println(tableFilter.contains("version"));
	}
	
	public static void saveToFile(String content,File path,String table,boolean isForm){
		String calssName = getBeanClassName(table,isForm);
		File file = new File(path+"/"+calssName+".java");
		
		if(!file.exists()){
			stringToFile(content, file);
			System.out.println( "Save to:" + file);
		}else{
			if(isForm && isRebuildForm){
				stringToFile(content, file);
				System.out.println( "Save to:" + file);
			}
			if(!isForm && isRebuildEntity){
				stringToFile(content, file);
				System.out.println( "Save to:" + file);
			}
		}		
	}
	
	public static String getBeanClassName(String table,boolean isForm){
		
		Pattern p = Pattern.compile("(_[a-zA-Z]{1})"); 
		Matcher m = p.matcher(table); 
		StringBuffer sbf = new StringBuffer();  
	 
		while (m.find()){ 
		m.appendReplacement(sbf, m.group(1).substring(1).toUpperCase());  
		} 
		m.appendTail(sbf);  
		table = sbf.toString(); 
		
		String tableFirst = table.substring(0,1).toUpperCase();
		String tableOther = null;
/*		if( table.endsWith("s") ){
			if(table.endsWith("ies")){
				tableOther  = table.substring(1, table.length()-3)+"y";
			}else{
				tableOther = table.substring(1, table.length()-1);
			}
		}else{
			tableOther = table.substring(1);
		}*/
		
		tableOther = table.substring(1);
		if(isForm)
			return tableFirst+tableOther+"Form";
		
		return tableFirst+tableOther;
	}
	
	public static String buildBean(Connection connection,String table,boolean isForm){
		String beanName = getBeanClassName(table,isForm);
		List<Col> cols = cols( connection,table );
		Hashtable<String, String> content = buildContent(table, cols);		
			
		StringBuffer entityContent = new StringBuffer();
		if(isForm){
			entityContent.append("package "+formPackage+";"+line+line);
		}else{
			entityContent.append("package "+entityPackage+";"+line+line);
		}
		entityContent.append(content.get(STR_ENTITY_IMPORT));
		if(isForm){
			entityContent.append(line);
			entityContent.append("import com.spring.web.formbean.Form;"+line+line);
			entityContent.append("public class "+beanName+" extends Form{"+line+line);
		}else{
			entityContent.append("import com.spring.annotation.entity.*;"+line);
			entityContent.append("import com.spring.bean.Entity;");
			entityContent.append(line).append(line);			
			entityContent.append("@Table(name=\""+table+"\")").append(line);
			entityContent.append("public class "+beanName+" extends Entity {"+line+line);
		}
		entityContent.append(content.get(STR_ENTITY_FIELD)+line);
		entityContent.append(content.get(STR_ENTITY_FUN)+line);
		
		entityContent.append("\t@Override"+line);
		entityContent.append("\tpublic String toString(){"+line);
		entityContent.append("\t\treturn \""+beanName);
		entityContent.append(content.get(STR_ENTITY_TOSTRING)+line);
		entityContent.append("}");
		
		return entityContent.toString();
	}
	
	private static Hashtable<String, String> buildContent(String table,List<Col> cols){
		StringBuffer importStr = new StringBuffer();
		StringBuffer fieldStr = new StringBuffer();
		StringBuffer funStr = new StringBuffer();
		StringBuffer toString = new StringBuffer(" [\"+"+line);		
		
		int tmp = toStringFieldLen;
		Map<String, String> importMap = new HashMap<String, String>();		
		for(Col col:cols){
			String fieldName = col.getColName();
			int fieldType = col.getType();
			String frist = fieldName.substring(0,1).toUpperCase();// 首字母
			String last = fieldName.substring(1);
			
			String typeName = getTypeName(fieldType);
			//设置属性
			fieldStr.append("\tprivate "+typeName+" "+fieldName+"; " + " // " +line);
			//get方法
			funStr.append("\tpublic "+typeName+" get"+frist+last+"(){"+line);
			funStr.append("\t\treturn "+fieldName+";"+line+"\t}"+line);
			//set方法
			funStr.append("\tpublic void set"+frist+last+"("+getTypeName(fieldType)+" "+fieldName+"){"+line);
			funStr.append("\t\tthis."+fieldName+" = " + fieldName+";"+line+"\t}"+line+line);
			
			String packageName = getTypePackageName(fieldType);
			importMap.put(packageName, packageName);
						
			if(tmp>1 && tmp<= toStringFieldLen){
				if(tmp == toStringFieldLen){
					toString.append("\t\t\t\"");
				}
				toString.append(fieldName+"=\"+"+fieldName+"+\", ");
				tmp--;
			}else{
				toString.append(fieldName+"=\"+"+fieldName+"+\", \"+"+line);
				tmp=toStringFieldLen;				
			}
		}
		if(tmp != toStringFieldLen){
			toString.append("\" + "+line);
		}
		toString.append("\t\t\t\"toString()=\"+super.toString() +\"]\";"+line+"\t}");
		Iterator<String> packageNames = importMap.keySet().iterator();
		while(packageNames.hasNext()){
			String value = packageNames.next();
			if(!StringUtils.isBlank(value))
			importStr.append("import "+value+";"+line);
		}		
		
		Hashtable<String, String> hash = new Hashtable<String, String>();
		hash.put(STR_ENTITY_IMPORT, importStr.toString());
		hash.put(STR_ENTITY_FIELD, fieldStr.toString());
		hash.put(STR_ENTITY_FUN, funStr.toString());
		hash.put(STR_ENTITY_TOSTRING, toString.toString());
		return hash;
	}
	
	static List<Col> cols(Connection connection, String table ){
		String sql = "select * from "+table+" where 1=2";
		PreparedStatement pstm = null;  
		ResultSetMetaData rsm = null;
		List<Col> cols = new ArrayList<Col>();
		try{
			pstm= connection.prepareStatement(sql);  
			pstm.executeQuery();  
			rsm=pstm.getMetaData();  
			for(int i=1;i<=rsm.getColumnCount();i++){
				String fieldName = rsm.getColumnName(i);
				int type = rsm.getColumnType(i);
				Col col = new Col(fieldName,type);
				cols.add(col);	
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
		return cols;
	}
	
	private static List<String> tables(Connection connection) throws Exception {
	    DatabaseMetaData meta = connection.getMetaData(); //获取数据库连接的元数据
	    //查询连接的所有Table(如果需要查询视图等,可以在最后的数组中添加VIEW...,依此类推)
	    ResultSet rsTable = meta.getTables(null, null, null, new String[] { "TABLE" });
	    //获取到的数据是以ResultSet形式返回
	    List<String> tables = new ArrayList<String>();
	    while (rsTable.next()) {
	    	String tableName = rsTable.getString(3);
	    	if( !tableFilter.contains(tableName) )
	    		tables.add( tableName );
	    }
	    return tables;
	}

	static Connection getConnection() throws SQLException {
		if (conn == null || conn.isClosed()) {
			try {
				Class.forName(driver);
				conn = DriverManager.getConnection(url, username, password);
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		return conn;
	}

	private static void loadPropery(){
		String propertyFileName = Configure.DEFAULT_PROPERTIES_FILE;
		Properties properties = null;
		
		try{
		InputStream in = Configure.class.getClassLoader().getResourceAsStream(propertyFileName); 
		if (in != null) {
			properties = new Properties();
			properties.load(in);
			
			//These should be null if not found
			url = properties.getProperty(Configure.PROPERTY_CONNECTION_URL);
			driver = properties.getProperty(Configure.PROPERTY_CONNECTION_DRIVER);
			username = properties.getProperty(Configure.PROPERTY_CONNECTION_USERNAME);
			password = properties.getProperty(Configure.PROPERTY_CONNECTION_PASSWORD);
			entityPackage = properties.getProperty(PROPERTY_BUILDER_ENTITY_PACKAGE);
			formPackage = properties.getProperty(PROPERTY_BUILDER_FORM_PACKAGE);
			srcPath = properties.getProperty(PROPERTY_BUILDER_SRC_PATH);
			String filterStr = properties.getProperty(PROPERTY_BUILDER_FILTER);
			String[] filters = filterStr.split(",");
			for(String filter:filters){
				tableFilter.add(filter);
			}
			
			String buildForm = properties.getProperty(PROPERTY_BUILDER_FORM);
			isBuildForm = BooleanUtils.toBoolean(buildForm);
			String rebuildForm = properties.getProperty(PROPERTY_BUILDER_FORM_EXIST);
			isRebuildForm = BooleanUtils.toBoolean(rebuildForm);
			
			String buildEntity = properties.getProperty(PROPERTY_BUILDER_ENTITY);
			isBuildEntity = BooleanUtils.toBoolean(buildEntity);
			String rebuildEntity = properties.getProperty(PROPERTY_BUILDER_ENTITY_EXIST);
			isRebuildEntity = BooleanUtils.toBoolean(rebuildEntity);
			
		} else {
			throw new RuntimeException ("Could not open an input stream on " + propertyFileName +".  Check that it is in the path " + System.getProperty("java.class.path"));
		}
		}catch (Exception e) {
			e.printStackTrace();
		}	
	}
	
	private static void stringToFile(String content,File fileWithpath){

		try {
			BufferedWriter out = new BufferedWriter(new FileWriter(fileWithpath));
			out.write(content);
			out.close();
		}catch (IOException e){
			e.printStackTrace();
		}
	}
	
	private static String getTypePackageName(int type){
		String packageName = null;		
		if(type == 91)
			packageName = "java.sql.Date";
		if(type == 92)
			packageName = "java.sql.Time";
		if(type == 93)
			packageName = "java.sql.Timestamp";
		
		return packageName;
	}
	
	private static String getTypeName(int type){
		if(type == 12 || type == -1 || type == 1 || type == -16 ) //VARCHAR,TEXT,CHAR
			return "String";
		if(type == 4 || type == -6 ) //INT,TINYINT
			return "Integer";
		if(type == -7 ) //Boolean,
			return "Boolean";
		if(type == 91)
			return "Date";
		if(type == 92)
			return "Time";
		if(type == 93) //TIMESTAMP
			return "Timestamp";
		if(type == 2 || type == 3 || type == 8)
			return "Double";
		if(type== 6 )
			return "Float";
		if(type == Types.BIGINT)
			return "Long";
		
		return "String";
	}
}

class Col{
	private String colName;
	private int type;	
	
	public Col(String colName,int type){
		this.colName = colName;
		this.type = type;
	}
	
	public String getColName() {
		return colName;
	}
	public void setColName(String colName) {
		this.colName = colName;
	}
	public int getType() {
		return type;
	}
	public void setType(int type) {
		this.type = type;
	}
}
